package cn.cachalot.oj.service.impl;

import cn.cachalot.oj.controller.param.SubmitParam;
import cn.cachalot.oj.dao.CompileResult;
import cn.cachalot.oj.dao.RunResult;
import cn.cachalot.oj.entity.ProblemCheck;
import cn.cachalot.oj.entity.Solution;
import cn.cachalot.oj.entity.Submit;
import cn.cachalot.oj.mapper.ProblemCheckMapper;
import cn.cachalot.oj.mapper.ProblemMapper;
import cn.cachalot.oj.mapper.SolutionMapper;
import cn.cachalot.oj.mapper.SubmitMapper;
import cn.cachalot.oj.service.SubmitService;
import cn.cachalot.oj.util.BaseContext;
import cn.cachalot.oj.util.CompileUtil;
import cn.cachalot.oj.util.R;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class SubmitServiceImpl extends ServiceImpl<SubmitMapper, Submit> implements SubmitService {
    @Resource
    ProblemMapper problemMapper;
    @Resource
    SubmitMapper submitMapper;
    @Resource
    ProblemCheckMapper problemCheckMapper;
    @Resource
    SolutionMapper solutionMapper;

    @Override
    @Transactional
    public R<Submit> submit(SubmitParam submitParam) {
        Integer language = submitParam.getLanguage();
        if (language < 0 || language > 4) {
            return R.error("语言错误!");
        }
        if (problemMapper.getProblemByProblemId(submitParam.getId()) == null) {
            return R.error("找不到题目");
        }
        Submit submit = new Submit();
        submit.setId(IdWorker.getId());
        submit.setLanguage(language);
        submit.setCode(submitParam.getCode());
        submit.setProblemId(submitParam.getId());
        submit.setStatus(0);
        submit.setUserId(BaseContext.getId());
        submit.setSubmitTime(LocalDateTime.now());
        submitMapper.insert(submit);
        return R.success().data(submit);
    }

    @Override
    @Transactional
    public R<String> compileAndRun(Submit submit) throws IOException, InterruptedException {
        Integer language = submit.getLanguage();
        String containerId = createContainer();
        CompileResult compileResult = CompileUtil.compile(language, submit.getCode(), containerId);
        if (!Objects.equals(compileResult.getErrorOutput(), "") && compileResult.getErrorOutput() != null) {
            submit.setStatus(2);
            submitMapper.updateById(submit);
            deleteContainer(containerId);
            return R.error("编译失败!").data(compileResult.getErrorOutput());
        }
        List<ProblemCheck> problemCheckList = problemCheckMapper.getProblemCheckListByProblemId(submit.getProblemId());
        RunResult runResult;
        for (ProblemCheck problemCheck : problemCheckList) {
            runResult = CompileUtil.run(language, problemCheck.getInput(), containerId);
            if (runResult.getTimeOut()) {
                submit.setStatus(3);
                submitMapper.updateById(submit);
                deleteContainer(containerId);
                return R.error("运行出错!").data("运行超时!");
            }
            if (!Objects.equals(problemCheck.getOutput(), runResult.getOutput())) {
                if (Objects.equals(runResult.getErrorOutput(), "")) {
                    submit.setStatus(4);
                    submitMapper.updateById(submit);
                    deleteContainer(containerId);
                    return R.error("答案错误!");
                } else {
                    submit.setStatus(3);
                    submitMapper.updateById(submit);
                    deleteContainer(containerId);
                    return R.error("运行出错!").data(runResult.getErrorOutput());
                }
            }
        }
        deleteContainer(containerId);
        submit.setStatus(1);
        submitMapper.updateById(submit);
        return R.success("验证通过!");
    }


    private String createContainer() throws IOException, InterruptedException {
        Process process =
                Runtime.getRuntime().exec("docker run -dit -m 10m --cpu-period=100000 --cpu-quota=45000 " + "  oj:1 ");
        StringBuilder outputBuilder = new StringBuilder();
        InputStream stdoutFrom = process.getInputStream();
        while (true) {
            int ch = stdoutFrom.read();
            if (ch == -1) {
                break;
            }
            outputBuilder.append((char) ch);
        }
        stdoutFrom.close();
        process.waitFor();
        return outputBuilder.toString().substring(0, 12);
//        return "0395a7a59fbe";
    }

    private void deleteContainer(String containerId) throws IOException, InterruptedException {
        String cmd = String.format("docker rm -f %s", containerId);
        Process process = Runtime.getRuntime().exec(cmd);
        process.waitFor();
    }
}
